 /*******************************************************************************
  * Copyright (c) 2004, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.core.resources.mapping;

 import java.util.ArrayList ;
 import org.eclipse.core.internal.resources.MarkerManager;
 import org.eclipse.core.internal.resources.Workspace;
 import org.eclipse.core.resources.*;
 import org.eclipse.core.runtime.CoreException;

 /**
  * A resource traversal is simply a set of resources and the depth to which
  * each is to be traversed. A set of traversals is used to describe the
  * resources that constitute a model element.
  * <p>
  * The flags of the traversal indicate which special resources should be
  * included or excluded from the traversal. The flags used are the same as
  * those passed to the {@link IResource#accept(IResourceVisitor, int, int)} method.
  *
  * <p>
  * This class may be instantiated or subclassed by clients.
  * </p>

  * @see org.eclipse.core.resources.IResource
  * @since 3.2
  */
 public class ResourceTraversal {

     private final int depth;
     private final int flags;
     private final IResource[] resources;

     /**
      * Creates a new resource traversal.
      * @param resources The resources in the traversal
      * @param depth The traversal depth
      * @param flags the flags for this traversal. The traversal flags match those
      * that are passed to the <code>IResource#accept</code> method.
      */
     public ResourceTraversal(IResource[] resources, int depth, int flags) {
         if (resources == null)
             throw new NullPointerException ();
         this.resources = resources;
         this.depth = depth;
         this.flags = flags;
     }

     /**
      * Visits all existing resources defined by this traversal.
      *
      * @param visitor a resource visitor
      * @exception CoreException if this method fails. Reasons include:
      * <ul>
      * <li> The visitor failed with this exception.</li>
      * </ul>
      */
     public void accept(IResourceVisitor visitor) throws CoreException {
         for (int i = 0, imax = resources.length; i < imax; i++)
             try {
                 if (resources[i].exists())
                     resources[i].accept(visitor, depth, flags);
             } catch (CoreException e) {
                 //ignore failure in the case of concurrent deletion
 if (e.getStatus().getCode() != IResourceStatus.RESOURCE_NOT_FOUND)
                     throw e;
             }
     }

     /**
      * Return whether the given resource is contained in or
      * covered by this traversal, regardless of whether the resource
      * currently exists.
      *
      * @param resource the resource to be tested
      * @return <code>true</code> if the resource is in this traversal, and
      * <code>false</code> otherwise.
      */
     public boolean contains(IResource resource) {
         for (int i = 0; i < resources.length; i++) {
             IResource member = resources[i];
             if (contains(member, resource)) {
                 return true;
             }
         }
         return false;
     }

     private boolean contains(IResource resource, IResource child) {
         if (resource.equals(child))
             return true;
         if (depth == IResource.DEPTH_ZERO)
             return false;
         if (child.getParent().equals(resource))
             return true;
         if (depth == IResource.DEPTH_INFINITE)
             return resource.getFullPath().isPrefixOf(child.getFullPath());
         return false;
     }

     /**
      * Efficient implementation of {@link #findMarkers(String, boolean)}, not
      * available to clients because underlying non-API methods are used that
      * may change.
      */
     void doFindMarkers(ArrayList result, String type, boolean includeSubtypes) {
         MarkerManager markerMan = ((Workspace) ResourcesPlugin.getWorkspace()).getMarkerManager();
         for (int i = 0; i < resources.length; i++)
             markerMan.doFindMarkers(resources[i], result, type, includeSubtypes, depth);
     }

     /**
      * Returns all markers of the specified type on existing resources in this traversal.
      * If <code>includeSubtypes</code> is <code>false</code>, only markers
      * whose type exactly matches the given type are returned. Returns an empty
      * array if there are no matching markers.
      *
      * @param type the type of marker to consider, or <code>null</code> to indicate all types
      * @param includeSubtypes whether or not to consider sub-types of the given type
      * @return an array of markers
      * @exception CoreException if this method fails.
      * @see IResource#findMarkers(String, boolean, int)
      */
     public IMarker[] findMarkers(String type, boolean includeSubtypes) throws CoreException {
         if (resources.length == 0)
             return new IMarker[0];
         ArrayList result = new ArrayList ();
         doFindMarkers(result, type, includeSubtypes);
         return (IMarker[]) result.toArray(new IMarker[result.size()]);
     }

     /**
      * Returns the depth to which the resources should be traversed.
      *
      * @return the depth to which the physical resources are to be traversed
      * (one of IResource.DEPTH_ZERO, IResource.DEPTH_ONE or
      * IResource.DEPTH_INFINITE)
      */
     public int getDepth() {
         return depth;
     }

     /**
      * Return the flags for this traversal.
      * The flags of the traversal indicate which special resources should be
      * included or excluded from the traversal. The flags used are the same as
      * those passed to the <code>IResource#accept(IResourceVisitor, int, int)</code> method.
      * Clients who traverse the resources manually (i.e. without calling <code>accept</code>)
      * should respect the flags when determining which resources are included
      * in the traversal.
      *
      * @return the flags for this traversal
      */
     public int getFlags() {
         return flags;
     }

     /**
      * Returns the file system resource(s) for this traversal. The returned
      * resources must be contained within the same project and need not exist in
      * the local file system. The traversal of the returned resources should be
      * done considering the flag returned by getDepth. If a resource returned by
      * a traversal is a file, it should always be visited. If a resource of a
      * traversal is a folder then files contained in the folder can only be
      * visited if the folder is IResource.DEPTH_ONE or IResource.DEPTH_INFINITE.
      * Child folders should only be visited if the depth is
      * IResource.DEPTH_INFINITE.
      *
      * @return The resources in this traversal
      */
     public IResource[] getResources() {
         return resources;
     }
 }

